#include "idt.h"
#include <string.h>
#include "dbgio.h"
#include "text_video.h"
#include "x86.h"
#include "pic.h"
#include "ksyscall.h"
#include "panic.h"
#define CODE_SEGMENT 0x8
#include "process.h"
idt_descriptor			x86_idt[IDT_MAX_DESCRIPTORS];
idt_pointer				x86_idt_pointer;
dri_interrupt_handler	dri_interrupt_dispatch[16];
process_info_t *		dri_interrupt_task	  [16];
static const char * const errornames[] = {"Divide by zero",	"Debugger Single-Step","NMI High","Debugger Breakpoint","Overflow",
					  "Bounds check", "Invalid instruction","Device not available","Double fault","Coprocessor Segment Overrun","Invalid TSS","Segment not present","Stack fault","General protection fault",
					  "Page fault","UNKNOWN","x87 FPU Error","Allignment Check","Machine check","SIMD FPU Exception"};

extern "C" void x86_syscall(registers_t regs){
	ksyscall_dispatch((syscall_parameter_block *)regs.esi);
}


void x86_load_idt(){
	__asm {
		lidt [x86_idt_pointer]
	}
}

void dri_register_interrupt(uint8_t irq, dri_interrupt_handler handler){
	dri_interrupt_task[irq] = get_current_process();
	dri_interrupt_dispatch[irq] = handler;
}

extern "C" void isr_handler(registers_t regs){
	if (regs.int_no <= 31)
		unhandled_interrupt(regs);//Exception
	else if (regs.int_no <= 47 &&
			 dri_interrupt_dispatch[regs.int_no - 32]
			 != ((dri_interrupt_handler) 0xFFFFFFFF)) {
		process_info_t *user_process = get_current_process();
		//if (regs.int_no != 32)
		//	dprintf("USR %i [pid:%i %s]\n",regs.int_no - 32,user_process->pid,user_process->name);
		select_task(dri_interrupt_task[regs.int_no - 32]);
		//if (regs.int_no != 32)
		//	dprintf("ISR %i [pid:%i %s]\n",regs.int_no - 32,dri_interrupt_task[regs.int_no - 32]->pid,dri_interrupt_task[regs.int_no - 32]->name);
		dri_interrupt_dispatch[regs.int_no - 32](regs);
		select_task(user_process);
		x86_interrupt_done (regs.int_no - 32);
	} else 
		unhandled_interrupt(regs);//HW

}


void unhandled_interrupt(registers_t regs){
	uint32_t ccr2;
	set_attrib(0xF,0x1);
	//clear_video();//disabled for debug
	position(0,0,1);
	dprintf("Project Atlantis has encountered an error and has been shut down\n to protect your pc\n");
	if (regs.int_no == 14){
		__asm {
			mov eax,cr2
			mov ccr2,eax
		}
		dprintf("ERROR : PAGE_FAULT\n");
		dprintf("\n\nDebugging information:\n");
		dprintf("Fault address: 0x%x, Fault Instruction: 0x%x\n",ccr2,regs.eip);
		dprintf("Flags : ");
		if (regs.err_code & 0x1)
			dprintf("PRESENT");
		else
			dprintf("NOT_PRESENT");
		if (regs.err_code & 0x2)
			dprintf(",WRITING");
		if (regs.err_code & 0x4)
			dprintf(",USER_MODE");
		if (regs.err_code & 0x8)
			dprintf(",RESERVED");		
		if (regs.err_code & 0x2)
			dprintf(",INSTR_FETCH");
	} else {
		if (regs.int_no > 19)
			dprintf("ERROR : UNHANDLED_INTERRUPT: 0x%x  CPU_ERROR: 0x%x\n",regs.int_no,regs.err_code);
		else
			dprintf("ERROR : %s CPU_ERROR:0x%x\n",errornames[regs.int_no],regs.err_code);
	}
	dprintf("Error occurred at 0x%x\n",regs.eip);
	for (;;);
}

void undefined_interrupt(){
	set_attrib(0xF,0x1);
	clear_video();
	position(0,0,1);
	dprintf("Codename PBX has encountered an error and has been shut down to protect your pc\nERROR : UNDEFINED_INTERRUPT\n");
	for (;;);
}

void x86_idt_initialize(){
	int c;
	tprintf("[0x%X] - _x86_idt_initialize() \n",(void *)x86_idt_initialize);
	x86_idt_pointer.m_base = (uint32_t) x86_idt;
	x86_idt_pointer.m_limit =(IDT_MAX_DESCRIPTORS * sizeof(idt_descriptor)) - 1;
	for (c = 0;c < IDT_MAX_DESCRIPTORS;c++)
		x86_idt_set_descriptor((uint8_t) c,undefined_interrupt,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	for (c = 0;c < 16;c++)
		dri_interrupt_dispatch[c] = ((dri_interrupt_handler) 0xFFFFFFFF);
	x86_idt_set_descriptor(0,isr0,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(1,isr1,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(2,isr2,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(3,isr3,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(4,isr4,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(5,isr5,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(6,isr6,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(7,isr7,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(8,isr8,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(9,isr9,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(10,isr10,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(11,isr11,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(12,isr12,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(13,isr13,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(14,isr14,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(15,isr15,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(16,isr16,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(17,isr17,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(18,isr18,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(19,isr19,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(20,isr20,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(21,isr21,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(22,isr22,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(23,isr23,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(24,isr24,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(25,isr25,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(26,isr26,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(27,isr27,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(28,isr28,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(29,isr29,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(30,isr30,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(31,isr31,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(32,isr32,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(33,isr33,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(34,isr34,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(35,isr35,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(36,isr36,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(37,isr37,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(38,isr38,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(39,isr39,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(40,isr40,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(41,isr41,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(42,isr42,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(43,isr43,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(44,isr44,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(45,isr45,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(46,isr46,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(47,isr47,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
	x86_idt_set_descriptor(0x80,isr_syscall,IDT_32BIT_INTERRUPT|IDT_USERMODE_MASK,CODE_SEGMENT);
	x86_load_idt();
	tprintf(" x86_idt_initialize: return\n");
}

void x86_idt_set_descriptor(uint8_t id, void *isr, uint8_t flags, uint32_t segment){
	idt_descriptor *idt_d = &x86_idt[id];
	uint32_t isr_i = (uint32_t) isr;
	if (id >= IDT_MAX_DESCRIPTORS)
		return;
	memset(idt_d,0,sizeof(idt_descriptor));
	idt_d->offsetLo		= (uint16_t) ( isr_i       & 0xFFFF);	
	idt_d->offsetHi		= (uint16_t) ((isr_i >> 16) & 0xFFFF);
	idt_d->segmentOrTSS = segment;
	idt_d->flags		= flags;
	//disable_scroll(0);
	//dprintf("ISR %i:Offset:%x,Offset Lo:%x,Offset Hi:%x,Segment:%x,Flags:%x\n",id,isr_i,idt_d->offsetLo,idt_d->offsetHi,segment,flags);
}

void register_hardware_isr(int irq,void *isr){
	x86_idt_set_descriptor(X86_IRQ_BASE + irq,isr,IDT_32BIT_INTERRUPT,CODE_SEGMENT);
}